package cn.wit.sst.components;

import java.util.List;
import java.util.stream.Collectors;

import cn.wit.sst.dto.ScoreRankDto;
import cn.wit.sst.ml.kmeans.Cluster;
import cn.wit.sst.ml.kmeans.KMeans;
import cn.wit.sst.ml.kmeans.Sample;
import cn.wit.sst.utils.Dialogs;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.control.Label;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.Pane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.TilePane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;

public class ChartHelper {
	/**
	 * 根据课程名称和对应的学生分数，创建一个表格
	 *
	 * @param course - 课程名称
	 * @param scores - 各学生的分数
	 *
	 * @return - 分数排名和统计图表
	 */
	public static Node createLineChart(String course, List<ScoreRankDto> scores) {
		LineChart<String, Number> chart;
		CategoryAxis xAxis;
		NumberAxis yAxis;

		xAxis = new CategoryAxis();
		xAxis.setTickLabelRotation(60d);
		yAxis = new NumberAxis();

		chart = new LineChart<>(xAxis, yAxis);
		chart.getStyleClass().add("score-chart");

		// 课程名作为标题
		chart.setTitle(course);
		chart.setLegendVisible(false);
		chart.setHorizontalGridLinesVisible(false);
		chart.setVerticalGridLinesVisible(false);
		chart.setPrefWidth(scores.size() * 48d);

		yAxis.setLabel("分数");

		// add starting data
		XYChart.Series<String, Number> series = new XYChart.Series<>();
		scores.forEach(score -> {
			Data<String, Number> data = new XYChart.Data<String, Number>(
					score.getStudent(), 	// 横坐标
					score.getScore()		// 纵坐标
			);
			Node node = createNodeLabel(score);	// 排名圆圈

			// 排名圆圈上的分数提示tooltip
			String tooltip = String.format("%s (%2.0f)", score.getStudent(), score.getScore());
			Tooltip.install(node, Dialogs.tooltip(tooltip));
			data.setNode(node);
			series.getData().add(data);
        });

		chart.getData().add(series);

		Pane chartPane = new Pane();
		chartPane.getChildren().add(chart);

		DraggableView dv = new DraggableView(createStatPane(scores), chart);
		dv.setLayoutX(100);
		dv.setLayoutY(150);
		chartPane.getChildren().add(dv);

		return chartPane;
	}

	// 求平均分
	private static String getAverage(List<ScoreRankDto> scores) {
		double average = scores.stream()
			.mapToDouble(ScoreRankDto::getScore).average().getAsDouble();

		return String.format("%4.2f", average);
	}

	public static void main(String[] args) {
		System.out.println(String.format("%4.2f%%", 18.88998));
	}

	// 求及格率
	private static String getPassRatio(List<ScoreRankDto> scores) {
		long pass = scores.stream()
			.filter(score -> score.getScore() > 59.8D)
			.count();

		double ratio = (double) pass / scores.size() * 100;

		return String.format("%4.2f%%", ratio);
	}

	// 求高分率
	private static String getHighScoreRatio(List<ScoreRankDto> scores) {
		List<Sample> samples = scores.stream()
			.map(score -> new Sample(new double[] {score.getScore()}))
			.collect(Collectors.toList());

		List<Cluster> clusters = new KMeans(3).cluster(samples);
		Cluster maxCluster = clusters.stream().max(
				(c1, c2) -> c1.getCenter().compareTo(c2.getCenter())
		).get();

		double ratio = maxCluster.getPoints().size() / (double) scores.size();

		return String.format("%.2f%%", ratio * 100);
	}

	// 求方差
	private static String getVariance(List<ScoreRankDto> scores) {
		double average = scores.stream().mapToDouble(ScoreRankDto::getScore).average().getAsDouble();
		double sum = scores.stream()
			.mapToDouble(ScoreRankDto::getScore)
			.map(score -> Math.pow(score - average, 2))
			.sum();

		return String.format("%4.2f", sum / scores.size());
	}

	// 创建统计面板，显示在图表上，统计面板可以在图表上拖动
	private static Parent createStatPane(List<ScoreRankDto> scores) {
		TilePane pane = new TilePane();
		pane.setPrefColumns(2);
		pane.setPrefWidth(150D);

		String styles = new StringBuffer()
				.append("-fx-border-color: red;")
				.append("-fx-padding: 10;")
				.append("-fx-border-style: dotted;")
				.append("-fx-border-width: 1px;")
				.toString();
		pane.setStyle(styles);

		String labelStyles = new StringBuffer()
				.append("-fx-border-color: #2e8b57;")
				.append("-fx-padding: 5 10 5 5;")
				.append("-fx-border-style: dotted;")
				.append("-fx-border-width: 0px 1px 0px 0px;")
				.toString();

		final String[] LABELS = {"班级人数", "及格率", "高分率", "平均分", "方差"};
		for (int i = 0; i < LABELS.length; i++) {
			Label label = new Label(LABELS[i]);
			label.setStyle(labelStyles);
			label.setPadding(new Insets(4, 10, 4, 0));
			pane.getChildren().add(label);
			TilePane.setAlignment(label, Pos.CENTER_RIGHT);
		}

		final String[] VALUES = {
				String.valueOf(scores.size()),
				getPassRatio(scores),
				getHighScoreRatio(scores),
				getAverage(scores),
				getVariance(scores)
		};
		for (int i = 0, index = 1; i < LABELS.length; i++, index += 2) {
			Label label = new Label(VALUES[i]);
			label.setPadding(new Insets(4, 0, 4, 10));
			pane.getChildren().add(index, label);
			TilePane.setAlignment(label, Pos.CENTER_LEFT);
		}

		return pane;
	}



	// 创建图表上面的“排名”圆圈
	private static Node createNodeLabel(ScoreRankDto score) {
		StackPane stackPane = new StackPane();

		Circle circle = new Circle(8d);
		circle.setStrokeWidth(1d);
		circle.setStroke(Color.DARKGRAY);
		circle.setFill(Color.WHITE);

		stackPane.getChildren().add(circle);

		Label label = new Label(score.getRank() + "");
		label.setStyle("-fx-font-size: 8; -fx-font-weight: bold;");

		label.setTextFill(Color.FORESTGREEN);

		stackPane.getChildren().add(label);
		return stackPane;
	}
}
